Getting Started

Python Interpreter
Basics
IPython
Author
Published

Saturday, June 1, 2024

🤓 CONTEXTO, CONFIGURACIÓN E INTERACCIÓN 🐍

Este curso es una introducción al trabajo con datos en Python, ofreciendo una visión general de las principales características de las estructuras de datos y librerías incorporadas para la manipulación de datos.

El enfoque principal del curso es el análisis de datos basados en tablas y la preparación de datos para conjuntos pequeños que puedan manejarse en un ordenador personal. Para utilizar estas herramientas, a veces es necesario organizar los datos desordenados en una forma tabular. Python es ideal para esto, y cuanto más familiarizado esté con el lenguaje y sus tipos de datos, más fácil será preparar los conjuntos de datos para su análisis.


⚙️ Configuración Inicial

Rstudio

Usaremos la IDE de Rstudio (claramente no nos limitaremos a ella, usaremos Jupyter Notebook, VSC, Google Colab, etc). Pero más adelante trabajaremos con ambos lenguajes (Python y R) simultáneamente por lo cual nos servirá realizar esta configuración.

  1. Primero necesita conocer con exactitud la ruta del ejecutable python.exe 💥 que desea usar (sea con Python, conda, venv, etc). Para ello abra la consola de su intérprete (si usa un environment en particular no olvide ingresar a él 📥) y ejecute los comandos mostrados. Guarde dicha dirección en el portapapeles 📎.
python
import sys
print(sys.executable)

  1. Ahora que conoce la dirección correcta de su intérprete de Python 📇, diríjase a la ruta mostrada en Rstudio. 👀 En ocasiones no le saldrá para ser seleccionado, por eso es mejor tener la ruta, péguela en la casilla y espere unos segundos que lo reconozca antes de salir y darle guardar o aplicar (si realiza lo anterior de forma muy rápida no le quedará). Le pedirá que reinicie sesión y ya deberá quedar si vuelve a ingresar.

  1. Por último instalaremos una librería que nos proporcionará un conjunto completo de herramientas para la interoperabilidad entre Python y R 🛠️.
install.packages("reticulate")

Conda

Como usaremos dentro de los .qmd el parámetro jupyter: python3, es necesario tener instalada dicha librería en su intérprete 💻, para lo cual ejecute (dentro de su kernel a usar y environment elegido):

conda install jupyter
# conda install numpy pandas scikit-learn matplotlib seaborn jupyter

Compruebe que efectivamente haya sido descargado con:

conda list

Quarto

Sí ejecuta el siguiente comando en el terminal ⌨️, deberá obtener algo como lo siguiente:

quarto check jupyter

Como puede observar tenemos un problema ⚠️ y es que no quedó el kernel que elegimos desde un inicio. Y el error mostrado se debe a que obviamente hay módulos que no hemos instalado en dicho intérprete que Quarto selecciona por defecto. Si desea obtener más información de dicho error consulte: Quarto Uses Wrong Python Version.

Para “corregir” lo anterior debemos apoyarnos de la documentación oficial, la cual indica que se debe crear un archivo _environment (sí así tal cual sin extensión alguna) y establecer los valores para QUARTO_PYTHON="C:\ProgramData\miniconda3\" ✔️. Así conseguimos que Quarto pueda establecer y fijar dicha configuración local para nuestro .qmd.

  • QUARTO_PYTHON se establece en: C:\ProgramData\miniconda3\
  • PY_PYTHON se establece en: C:\ProgramData\miniconda3\
  • RETICULATE_PYTHON se establece en: C:\ProgramData\miniconda3\

El siguiente blog 📔 explica demasiado bien lo que acabamos de hacer a mayor detalle, para renderizar un documento Quarto con un entorno específico en RStudio/Posit.

🟢 Finalmente, cree y ejecute un nuevo .qmd, puede utilizar la plantilla ejemplo, y agregue el siguiente chunk para que verifique por su propia cuenta que, a pesar de que Quarto a nivel global está usando otro intérprete, al definir el _environment queda con el que usted especifica.

import sys
print(sys.executable)
print(sys.prefix)
print (sys.version)
C:\ProgramData\miniconda3\pythonw.exe
C:\ProgramData\miniconda3
3.12.2 | packaged by Anaconda, Inc. | (main, Feb 27 2024, 17:28:07) [MSC v.1916 64 bit (AMD64)]

🤖 IPython

🔖 Cualquier comando estándar de shell puede ser utilizado directamente en IPython anteponiendo el carácter !. Por ejemplo,

# : ....................... Shell Commands en IPython ........................ :
!ls
Books
LICENSE
Profile.jpg
Python Course.Rproj
_environment
_freeze
_publish.yml
_quarto.yml
_site
about.qmd
files
index.qmd
posts
styles.css
directory = !pwd
print(directory)
['/d/Documentos/JOBS/DNPE/CursoPython']
message = "Imprimiendo un texto desde shell"
!echo {message}
Imprimiendo un texto desde shell

Conceptos Básicos

  • 📐 Identación (no llaves {} ): Python utiliza espacios en blanco (tabuladores o espacios) para estructurar el código en lugar de utilizar llaves como en muchos otros lenguajes como R y C++.
  • 🔣 No se necesita terminar con ;: El punto y coma puede usarse para separar múltiples sentencias en una sola línea. a = 1; b = 2; c = 3
  • 🏷️ Las cadenas admiten tanto comillas simples ' como comillas dobles ": Se prefieren las comillas dobles, y para cadenas de varias líneas con saltos de línea, puede utilizar comillas triples """.
  • 🆎 Vinculación (binding): En otros lenguajes, la asignación hará que se copien los datos. En Python, hace que se refieran ahora al mismo objeto,
a = [1, 2, 3]
b = a
a.append(4)
b
[1, 2, 3, 4]
# Cuando se pasan objetos como argumentos a una función, se crean nuevas variables
# locales que hacen referencia a los objetos originales sin necesidad de copiarlos
def myAppend(lista, elemento):
    lista.append(elemento)
data = [1, 2, 3]
myAppend(data, 4)
data
[1, 2, 3, 4]
  • 💪 Un lenguaje tipado: Cada objeto tiene un tipo (o clase) específico, con la ventaja de que las variables no tienen un tipo inherente asociado a ellas.
a = 5      ; print(type(a))
a = "cinco"; print(type(a))
<class 'int'>
<class 'str'>
"5" + 5
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[8], line 1
----> 1 "5" + 5

TypeError: can only concatenate str (not "int") to str
a = 4.5
b = 2
print(a / b)
print(isinstance(a, (int, float)))
2.25
True

Help & Time

# Acceso a la Documentación:
help(len)
Help on built-in function len in module builtins:

len(obj, /)
    Return the number of items in a container.
letra = "a"
# letra.<Press Tab>
getattr(letra, "split")
<function str.split(sep=None, maxsplit=-1)>
# Guión bajo '_' para salidas anteriores
1+1
2+2
print(_)
<built-in method split of str object at 0x00007FF807D71CE0>
# Acceder a las entradas anteriores (historial)
%history -n 1-3
   1:
import sys
print(sys.executable)
print(sys.prefix)
print (sys.version)
   2:
# : ....................... Shell Commands en IPython ........................ :
!ls
   3:
directory = !pwd
print(directory)
# : .......................... Timing Code Snippets .......................... :
%timeit sum(range(100))   # IPython Magic Commands
356 ns ± 1.58 ns per loop (mean ± std. dev. of 7 runs, 1,000,000 loops each)
%%timeit                  # Cuando es más complejo, baja el número de repeticiones
total = 0
for i in range(1000):
    for j in range(1000):
        total += i * (-1) ** j
84.9 ms ± 2.39 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

Imports

```{python}
# Modulo.py
PI = 3.14159
def cuadrado(x):
 return x ** 2
```
import Modulo
print(Modulo.cuadrado(5))
pi = Modulo.PI
# ......................................
from Modulo import PI, cuadrado
cuadrado(PI)
# ......................................
import Modulo as m
from Modulo import PI as kte, cuadrado as elevado

r1 = m.cuadrado(kte); print(r1)
r2 = elevado(6)     ; print(r2)

Operadores Binarios y de Comparación

a = [1, 2, 3]
b = a
c = list(a) # Crea una copia

print(a is b)
print(a is not c)
True
True
a.append(4)
a == c
False
# Intercambiar los nombres de las variables:
a, b = 1, 2
# tmp = a
# a = b
# b = tmp
b, a = a, b
print(f'a = {a}, b = {b}')
a = 2, b = 1

ℹ️ Tipos Estándar

Tipo Descripción
None El valor “nulo” de Python (solo existe una instancia del objeto None)
str Tipo de cadena; contiene cadenas Unicode
bytes Datos binarios en bruto
float Número de punto flotante de doble precisión (nota que no hay un tipo double separado)
bool Un valor booleano True o False
int Entero de precisión arbitraria

Booleans

print(int(False)); print(int(True))
print(True + False)
print(not not not not True)
print(bool(2024.75)) # Todo valor !=0 se convierte en True
print(bool("."))     # Todo valor !="" se convierte en True
0
1
1
True
True
True

Strings

a = "Hola mundo \\>"
print(a)
Hola mundo \>
b = """
Esta cadena es más larga que
abarca varias líneas
"""
print(b)
b.count("\n")

Esta cadena es más larga que
abarca varias líneas
3
c = r"D:\Documentos\Carpeta"
print(c)
D:\Documentos\Carpeta
# Los strings en Python son inmutables: no se puede modificar una cadena
print(a[5])
a[5] = "M"
m
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[23], line 3
      1 # Los strings en Python son inmutables: no se puede modificar una cadena
      2 print(a[5])
----> 3 a[5] = "M"

TypeError: 'str' object does not support item assignment
a.replace("m", "M")
'Hola Mundo \\>'
a       # Tras esta operación, la variable no se modifica
'Hola mundo \\>'
list(a) # Las cadenas son una secuencia de caracteres Unicode
['H', 'o', 'l', 'a', ' ', 'm', 'u', 'n', 'd', 'o', ' ', '\\', '>']

Formatted String

Existen varios métodos para formatear cadenas de texto, entre los cuales encontramos:

  1. Operador de Formato %
Nombre = "Juan"
Edad = 33
Pi   = 3.141592
print("Hola, mi nombre es %s y tengo %d años. Y sé que pi vale: %.2f." % (Nombre, Edad, Pi))
Hola, mi nombre es Juan y tengo 33 años. Y sé que pi vale: 3.14.
  1. Método .format()
print("Hola, mi nombre es {} y tengo {} años. Y sé que pi vale: {:.2f}.".format(Nombre, Edad, Pi))
print("Nombre: {name}, Edad: {age}".format(name = "Luis", age = 33))
print("El {0} se encuentra en el {1} y el {1} se encuentra en el {0}.".format("sol", "cielo"))
Hola, mi nombre es Juan y tengo 33 años. Y sé que pi vale: 3.14.
Nombre: Luis, Edad: 33
El sol se encuentra en el cielo y el cielo se encuentra en el sol.
# 0:.3f El 1re valor será un flotantes con 3 decimales
# 1:s   El 2do valor será un string
# 2:d   El 3re valor será un entero
template = "{0:.3f} {1:s} equivalen a US${2:d}"
template.format(3.960, "pesos Colombianos", 1) # Intente modificar el tipo de alguno de estos
'3.960 pesos Colombianos equivalen a US$1'
  1. f-strings
Tasa   = 3.960
Moneda = "Colombianos"
f"{Tasa:.4f} pesos {Moneda} equivalen a US${Tasa/Tasa}"
'3.9600 pesos Colombianos equivalen a US$1.0'
# Cálculos Dentro:
a = 5; b = 10
print(f"La suma de {a} y {b} es {a + b}. Además, a*b = {a*b}")
La suma de 5 y 10 es 15. Además, a*b = 50
# Alinear Texto:
print(f"{'izquierda':<10} {'centrado':^10} {'derecha':>10}")
izquierda   centrado     derecha
# Mostrar Números en Diferentes Bases:
Numero = 255
print(f"Decimal: {Numero}, Binario: {Numero:b}, Hexadecimal: {Numero:x}, Octal: {Numero:o}")
Decimal: 255, Binario: 11111111, Hexadecimal: ff, Octal: 377
# Incluir Expresiones y Métodos:
Txt = "Amo Python"
print(f"El texto en mayúsculas es: {Txt.upper()}.")
El texto en mayúsculas es: AMO PYTHON.
# Combinación con Diccionarios:
Pais = {"nombre": "Colombia", "NoPoblacion": 50}
print(f"País: {Pais['nombre']} | Habitantes: {Pais['NoPoblacion']}")
País: Colombia | Habitantes: 50
# Incluir Llaves Literales:
print(f"Este es un valor literal en llaves: {{Numero}}, y este es el valor de la variable: {Numero}.")
Este es un valor literal en llaves: {Numero}, y este es el valor de la variable: 255.
  1. Método string.Template
from string import Template
template = Template("Hola, mi nombre es $name y tengo $age años.")
mensaje  = template.substitute(name = "Juan", age = 33)
print(mensaje)
Hola, mi nombre es Juan y tengo 33 años.
Método Sintaxis Ventajas Desventajas
Operador % Hola, %s % “mundo” Simple y familiar para quienes conocen C Menos flexible y más propenso a errores
Método .format() Hola, {}.format(“mundo”) Muy flexible, soporta nombres y posiciones Sintaxis más verbosa
f-strings f"Hola, {variable}" Muy legible, permite expresiones dentro de {} Disponible solo en Python 3.6+
string.Template Template("Hola, $nombre") Simple y seguro, fácil de leer Menos potente para formateo complejo

None

Abc.

Dates and times

from datetime import datetime, date, time

dt = datetime(2024, 1, 31, 20, 30, 21)
dt.day
31
dt.date()
datetime.date(2024, 1, 31)
dt.time()
datetime.time(20, 30, 21)
dt.strftime("%Y-%m-%d %H:%M")
'2024-01-31 20:30'
dt_hour = dt.replace(minute = 0, second = 0)
dt_hour
datetime.datetime(2024, 1, 31, 20, 0)
delta = datetime.now() - dt
delta
datetime.timedelta(days=181, seconds=80865, microseconds=14995)

🚩 Objetos Mutables e Inmutables

Tuplas

Note

Una tupla es una secuencia ordenada de objetos de longitud fija e inmutable (una vez asignada no puede modificarse). La forma más sencilla de crear una tupla es con una secuencia de valores separados por comas entre paréntesis.

tup1 = (1, 2, 3)      # Pueden contener elementos repetidos
tup2 = 4, 5, 6        # Los paréntesis pueden omitirse
print(tup1); print(tup2)
(1, 2, 3)
(4, 5, 6)
# Se puede convertir cualquier secuencia o iterador
print( tuple([7, 8, 9]) )
print( tuple("Texto") )
(7, 8, 9)
('T', 'e', 'x', 't', 'o')
tupCompleja = (2, 4, 8), (3, 5)
print(tupCompleja)
tupCompleja[0]
((2, 4, 8), (3, 5))
(2, 4, 8)
tupMixta = tuple(['txt', [1, 2], True])
tupMixta[1].append(3) # Si un objeto dentro de una tupla es mutable, se puede modificar
print(tupMixta)
tupMixta[2] = False
('txt', [1, 2, 3], True)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[47], line 4
      2 tupMixta[1].append(3) # Si un objeto dentro de una tupla es mutable, se puede modificar
      3 print(tupMixta)
----> 4 tupMixta[2] = False

TypeError: 'tuple' object does not support item assignment
print( tup1*2 + (20, 24) + ('year',) )
(1, 2, 3, 1, 2, 3, 20, 24, 'year')
# Desempaquetado (unpacking)
uno, dos, tres = tup1
print(uno)
(a, b, c), (d, e) = tupCompleja
print(a)
1
2
seq = [(1, 2, 3), (4, 5, 6), (7, 8, 9)]
for a, b, c in seq:
    print(f'a = {a}, b = {b}, c = {c}')
a = 1, b = 2, c = 3
a = 4, b = 5, c = 6
a = 7, b = 8, c = 9
first, second, *rest = (1, 2, 3, 4, 5)
print(f"{first} || {second} || {rest}")
# El guión bajo (_) para descartar las variables no deseadas
first, second, *_ = (1, 2, 3, 4, 5)
print(*_)
1 || 2 || [3, 4, 5]
3 4 5
# Si desea definir una tupla con un solo elemento debe incluir una coma al final:
my_t = (3,)
print(my_t)
# Métodos
(1, 0, 1, 1, 0).count(0)
(3,)
2

Listas

Note

Una lista es una colección de elementos en un orden determinado. Puedes poner lo que quieras en una lista, y los elementos que la componen no tienen porqué estar relacionados de ninguna manera en particular. En Python, los corchetes [ ] indican una lista, y los elementos individuales de la lista están separados por comas.

Warning
  • Las posiciones de índice empiezan en 0, no en 1.
  • Las listas son de longitud variable y su contenido puede modificarse (son mutables).
motorcycles = [0, "honda", "yamaha", "suzuki", None]
print(motorcycles)
list(tup1)
[0, 'honda', 'yamaha', 'suzuki', None]
[1, 2, 3]
# Añadir y eliminar elementos
motorcycles.append("ducati")
motorcycles.insert(1, "1")
del motorcycles[-2]         # Removing an Item Using the del Statement
motorcycles.pop(0)          # Popping Items from Any Position
print(motorcycles)
['1', 'honda', 'yamaha', 'suzuki', 'ducati']
Caution

La inserción (insert) es más costosa computacionalmente que la adición (append), ya que las referencias a los elementos subsiguientes deben desplazarse internamente para dejar espacio al nuevo elemento.

motorcycles.append("2"); print(motorcycles)
motorcycles.remove("2"); print(motorcycles) # Removing an Item by Value
['1', 'honda', 'yamaha', 'suzuki', 'ducati', '2']
['1', 'honda', 'yamaha', 'suzuki', 'ducati']
# Sorting a List
motorcycles.sort(); print(motorcycles)
motorcycles.sort(reverse = True); print(motorcycles)
print(sorted(motorcycles)); print(motorcycles)
['1', 'ducati', 'honda', 'suzuki', 'yamaha']
['yamaha', 'suzuki', 'honda', 'ducati', '1']
['1', 'ducati', 'honda', 'suzuki', 'yamaha']
['yamaha', 'suzuki', 'honda', 'ducati', '1']
print(motorcycles.reverse())
len(motorcycles)
None
5
# Python returns another index error
listA = []
print(listA[-1])
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
Cell In[58], line 3
      1 # Python returns another index error
      2 listA = []
----> 3 print(listA[-1])

IndexError: list index out of range
squares = []
for value in range(1, 11):
    squares.append(value ** 2)
print(squares)
# List Comprehensions
squares = [value**2 for value in range(1, 11)]
print(squares)
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
# Slicing a List
print(motorcycles[0:3])
print(motorcycles[2:])
print(motorcycles[-2:])
['1', 'ducati', 'honda']
['honda', 'suzuki', 'yamaha']
['suzuki', 'yamaha']
# Copying a List
onces = ["pizza", "arepa", "empanada"]
tusOnces = onces[:]
onces.append("buñuelo")
tusOnces.append("pan")
print(onces); print(tusOnces)
['pizza', 'arepa', 'empanada', 'buñuelo']
['pizza', 'arepa', 'empanada', 'pan']

Diccionarios

Note

Un diccionario (conocido en otros lenguajes como maps o associative arrays) almacena una colección de pares clave-valor, donde clave y valor son objetos Python. Cada clave está asociada a un valor, de modo que un valor puede ser convenientemente recuperado, insertado, modificado o eliminado dada una clave en particular. Tenga presente que las claves únicas (no permiten duplicados en las claves).

fisico = {'color': 'blanco', 'altura': 1.79}
fisico["ojos"] = "azules"
fisico["peso"] = 87
print(fisico)
{'color': 'blanco', 'altura': 1.79, 'ojos': 'azules', 'peso': 87}
# Diccionario con tuplas como claves
puntos = {
    (0, 0): "Origen",
    (1, 2): "Punto A",
    (3, 4): "Punto B"
}
print(puntos[(1, 2)])
Punto A

Conjuntos

Abc.

# Mutable
mi_lista = [1, 2, 3]
print(id(mi_lista))  # Ejemplo de ID: 2180570982400
mi_lista.append(4)
print(id(mi_lista))  # Mismo ID: 2180570982400

# Inmutable
mi_tupla = (1, 2, 3)
print(id(mi_tupla))  # Ejemplo de ID: 2180571003840
mi_tupla = (1, 2, 3, 4)
print(id(mi_tupla))  # Nuevo ID: 2180571086992
1610394567040
1610394567040
1610394610496
1610394566256

Flujo de Control

if, elif, and else

w = 5; x = 4; y = 3; z = 2
if x < 0:
    print("x es negativo.")
elif x == 0:
    print("x es cero")
elif w > x > y > z:
    print("Los valores están en orden.")
else:
    print("Otro caso diferente.")
Los valores están en orden.

for

numeros = [1, 2, None, 4, None, 5] # sum(numeros)
total = 0
for value in numeros:
    if value is None:
        continue
    if value == 4:
        break       # La sentencia 'break' se usa para salir del bucle
    total += value
total
3
# 'break' sólo termina el for más interno, cualquier for externo continuará
for i in range(4):
    for j in range(4):
        if j > i:
            break
        print((i, j))
(0, 0)
(1, 0)
(1, 1)
(2, 0)
(2, 1)
(2, 2)
(3, 0)
(3, 1)
(3, 2)
(3, 3)

while

Esta estructura permite ejecutar repetidamente un bloque de código mientras una condición especificada se evalúa como verdadera. Este ciclo es útil cuando no se sabe de antemano cuántas veces se necesitará repetir el bloque de código, a diferencia del ciclo for, que itera sobre una secuencia de elementos de longitud conocida.

while True:
    entrada = input("Escribe 'salir' para terminar: ")
    if entrada.lower() == "salir":
        break

pass

Es la sentencia no-op (o no hacer nada) de Python. Puede usarse en bloques en los que no se va a realizar ninguna acción. Sólo es necesaria porque Python utiliza espacios en blanco para delimitar los bloques.

Cuando la ejecución del programa alcanza una sentencia continue, la ejecución del programa salta inmediatamente al inicio del bucle y reevalúa la condición del bucle.

while True:
    print('¿Quién eres tú?')
    name = input()
    if name != 'Juan':
        continue
    print('Hola Juan, cuál es tu contraseña (es un equipo)')
    password = input()
    if password == 'nacional':
        break
print('Acceso Concedido.')
  • Pass (for, if): No realiza ninguna acción y se utiliza como marcador de lugar (placeholder para futura implementación).
  • Continue (for, while): Salta a la siguiente iteración del bucle, omitiendo el resto del código en la iteración actual.
  • Break (if): Termina el bucle inmediatamente, saliendo de la estructura de control de flujo.

range

print(f"{   range(10)   }")
print(f"{   range(1, 10)   }")
print(f"{   list(range(0, 20, 2))   }")
print(f"{   list(range(5, -1, -1))   }")
range(0, 10)
range(1, 10)
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
[5, 4, 3, 2, 1, 0]

Funciones

Las funciones en Python son bloques de código reutilizables que permiten estructurar y organizar mejor los programas. Se definen usando la palabra clave def y pueden aceptar argumentos y devolver valores.

# Py añade return None al final de cualquier definición de función sin sentencia return:
def Saludo(nombre):
    print("Hola " + nombre)

spam = Saludo("Carlos")
None == spam
Hola Carlos
True

En un programa de Python, hay exactamente un único ámbito global. Este ámbito global es el contexto en el que las variables globales se definen y existen a lo largo de la ejecución del programa.

  • En cuanto a los ámbitos locales, el número de estos puede variar. Cada vez que se define y se llama a una función, se crea un nuevo ámbito local. Por lo tanto, el número de ámbitos locales depende del número de funciones (incluidas las funciones anidadas) y métodos que se llaman durante la ejecución del programa.
# Las variables locales no pueden utilizarse en el ámbito global:
def getPI():
    PI = 3.1415
getPI()
print(PI)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Cell In[70], line 5
      3     PI = 3.1415
      4 getPI()
----> 5 print(PI)

NameError: name 'PI' is not defined
# Los ámbitos locales no pueden utilizar variables de otros ámbitos locales:
def a():
    phone = 444
    b()
    print(phone)
def b():
    phone = 555
a()
444
# Las variables globales pueden leerse desde un ámbito local:
def a():
    print(cte)
cte = 1234
a()
print(cte)
1234
1234
# Si necesitas modificar una variable global desde dentro de una función:
def a():
    global cte
    cte = 'Ahora soy una Variable Global'

cte = 'Global'
a()
print(cte)
Ahora soy una Variable Global
# Funciones con Valores por Defecto:
def saludar(nombre = "Paola"):
    print(f"¡Hola, {nombre}!")

saludar()
saludar("Pedro")
¡Hola, Paola!
¡Hola, Pedro!
# Funciones con Varios Valores de Retorno:
def operaciones(a, b):
    suma  = a + b
    resta = a - b
    return suma, resta

suma, resta = operaciones(10, 2)
print(f"Suma: {suma}\t| Resta: {resta}")
Suma: 12    | Resta: 8
# Evitar que un programa se bloquee cuando recibe un error:
def a(b):
    try:
        return 1/b
    except ZeroDivisionError:
        print('Error: b debe ser diferente de 0.')
print(a(2))
print(a(0))
0.5
Error: b debe ser diferente de 0.
None

*args

El parámetro args se usa en las funciones para pasar un número variable de argumentos posicionales. Dentro de la función, args es una tupla que contiene todos los argumentos posicionales proporcionados.

Características:

  • Permite pasar una cantidad variable de argumentos a una función.
  • Los argumentos se agrupan en una tupla dentro de la función.
def imprimir_argumentos(*args):
    for i, arg in enumerate(args):
        print(f"Argumento {i}: {arg}")

imprimir_argumentos('a', 'b', 'c')
Argumento 0: a
Argumento 1: b
Argumento 2: c

**kwargs

El parámetro kwargs se usa para pasar un número variable de argumentos con nombre (palabra clave). Dentro de la función, kwargs es un diccionario que contiene todos los argumentos con nombre proporcionados.

Características:

  • Permite pasar una cantidad variable de argumentos con nombre a una función.
  • Los argumentos se agrupan en un diccionario dentro de la función.
def mostrar_info(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")

mostrar_info(nombre = "Alice", edad = 30, ciudad = "Nueva York")
nombre: Alice
edad: 30
ciudad: Nueva York
# Ejemplo combinado:
def combinar(*args, **kwargs):
    print("Argumentos posicionales:", args)
    print("Argumentos con nombre:", kwargs)

combinar(1, 2, 3, a = "Uno", b = "Dos", c = "Tres")
Argumentos posicionales: (1, 2, 3)
Argumentos con nombre: {'a': 'Uno', 'b': 'Dos', 'c': 'Tres'}

Funciones Lambda

Son funciones anónimas y de una sola línea que pueden ser definidas usando la palabra clave lambda argumentos: expresión. Son especialmente útiles cuando se necesita una función pequeña y desechable que se puede definir en una expresión.

cuadrado = lambda x: x ** 2
print(cuadrado(5))

multiplicar = lambda x, y: x * y
print(multiplicar(4, 6))
25
24

Uso con Funciones Integradas

Las funciones lambda son especialmente útiles cuando se combinan con ciertas funciones integradas de Python.

  1. map(): Aplica una función a cada ítem de un iterable (como una lista) y devuelve un nuevo iterable con los resultados.
numeros = list(range(11))
cuadrados = map(lambda x: x**2, numeros)
print(list(cuadrados))
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
  1. filter(): Filtra un iterable, permitiendo sólo los ítems que coincidan con una condición.
pares = filter(lambda x: x%2 == 0, numeros)
print(list(pares))
[0, 2, 4, 6, 8, 10]
print(list(filter(lambda x: (x % 2 == 0) and (x > 7), numeros)))
[8, 10]
  1. reduce(): Aplica una función a los ítems de un iterable, acumulando el resultado.
from functools import reduce

suma_total = reduce(lambda x,y: x + y, numeros)
print(suma_total)
55
  1. sorted(): Las funciones lambda pueden ser usadas para personalizar el ordenamiento de listas.
coordenadas = [(1, 2), (3, 4), (5, 1), (7, 3)]
ordenado = sorted(coordenadas, key = lambda x: x[1])
print(ordenado)
[(5, 1), (1, 2), (7, 3), (3, 4)]
Notas = [
    {'Nombre': 'Ana', 'Definitiva': 8},
    {'Nombre': 'Juan', 'Definitiva': 6},
    {'Nombre': 'Maria', 'Definitiva': 9}
]
print(sorted(Notas, key = lambda x: x['Definitiva']))
[{'Nombre': 'Juan', 'Definitiva': 6}, {'Nombre': 'Ana', 'Definitiva': 8}, {'Nombre': 'Maria', 'Definitiva': 9}]

Limitaciones

  • Una sola expresión: Las lambdas están limitadas a una única expresión. No pueden contener declaraciones ni múltiples líneas de código.
  • Legibilidad: Aunque útiles para funciones cortas y simples, las lambdas pueden hacer el código más difícil de leer si se usan en exceso o de manera compleja.

Decoradores

Los decoradores en Python son una herramienta poderosa que permite modificar el comportamiento de una función o método. Los decoradores permiten envolver una función en otra función, lo que facilita añadir funcionalidades adicionales de una manera muy elegante y reutilizable.

def decorador(func):
    def envoltura():
        print("Antes de la función")
        func()
        print("Después de la función")
    return envoltura
@decorador
def f():
    print("Función original")
f()
Antes de la función
Función original
Después de la función
# Registro de Llamadas a Funciones:
def registrar(func):
    def envoltura(*args, **kwargs):
        print(f"Llamando a {func.__name__} con {args} y {kwargs}")
        resultado = func(*args, **kwargs)
        print(f"{func.__name__} retornó {resultado}")
        return resultado
    return envoltura

@registrar
def suma(a, b):
    return a + b

suma(3, 5)
Llamando a suma con (3, 5) y {}
suma retornó 8
8
# Contador de Llamadas a una Función:
def contador_de_llamadas(func):
    def wrapper(*args, **kwargs):
        wrapper.llamadas += 1
        print(f"Llamada número: {wrapper.llamadas}")
        return func(*args, **kwargs)
    wrapper.llamadas = 0
    return wrapper

@contador_de_llamadas
def mi_funcion():
    print("Ejecutando la función")

mi_funcion()
mi_funcion()
Llamada número: 1
Ejecutando la función
Llamada número: 2
Ejecutando la función
# Medición de Tiempo de Ejecución:
import time
def medir_tiempo(func):
    def envoltura(*args, **kwargs):
        inicio = time.time()
        resultado = func(*args, **kwargs)
        fin = time.time()
        print(f"{func.__name__} tomó {fin - inicio} segundos")
        return resultado
    return envoltura

@medir_tiempo
def contar_hasta(numero):
    for i in range(numero):
        pass
    return numero

contar_hasta(1000000)
contar_hasta tomó 0.018638134002685547 segundos
1000000
# Memorización de Resultados:
def memoize(func):
    cache = {}
    def envoltura(*args):
        if args in cache:
            return cache[args]
        resultado = func(*args)
        cache[args] = resultado
        return resultado
    return envoltura

@memoize
def fibonacci(n):
    if n <= 1:
        return n
    return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(30))
832040
# Decoradores con Parámetros:
def repetir(veces):
    def decorador(func):
        def envoltura(*args, **kwargs):
            for _ in range(veces):
                func(*args, **kwargs)
        return envoltura
    return decorador

@repetir(3)
def saludar():
    print("¡Hola!")

saludar()
¡Hola!
¡Hola!
¡Hola!

Documentación de funciones

En Python, la documentación de funciones se suele escribir utilizando docstrings, que son cadenas de texto que describen el propósito y el uso de una función, método, clase o módulo.

def nombre_funcion(param1, param2):
    """
    Descripción breve de lo que hace la función.

    Args:
        param1 (tipo): Descripción de param1.
        param2 (tipo): Descripción de param2.

    Returns:
        tipo: Descripción de lo que devuelve la función.

    Raises:
        ExceptionType: Descripción de las excepciones que puede lanzar la función.
    """
    pass
def division(a, b):
    """
    Divide a entre b.

    Args:
        a (float): El numerador.
        b (float): El denominador.

    Returns:
        float: El resultado de la división.

    Examples:
        >>> division(1, 2)
        0.5
        >>> division(1, 10)
        0.1

    Raises:
        ValueError: Si b es cero.
        
    """
    if b == 0:
        raise ValueError("El denominador no puede ser cero.")
    return a / b

help(division)
division(1, 0)
Help on function division in module __main__:

division(a, b)
    Divide a entre b.

    Args:
        a (float): El numerador.
        b (float): El denominador.

    Returns:
        float: El resultado de la división.

    Examples:
        >>> division(1, 2)
        0.5
        >>> division(1, 10)
        0.1

    Raises:
        ValueError: Si b es cero.
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[93], line 27
     24     return a / b
     26 help(division)
---> 27 division(1, 0)

Cell In[93], line 23, in division(a, b)
      2 """
      3 Divide a entre b.
      4 
   (...)
     20     
     21 """
     22 if b == 0:
---> 23     raise ValueError("El denominador no puede ser cero.")
     24 return a / b

ValueError: El denominador no puede ser cero.

Convenciones de Formato

Hay varias convenciones para escribir docstrings. La más común en Python es la convención de estilo Google, pero también existen otras como el estilo NumPy/SciPy y el estilo reStructuredText (reST), que es utilizado por Sphinx para generar documentación.

Estilo Google

def f(param1, param2):
    """
    Una breve descripción de la función.

    Args:
        param1 (int): El primer parámetro.
        param2 (str): El segundo parámetro.

    Returns:
        bool: El valor de retorno.
    """
    pass

Estilo NumPy/SciPy

def f(param1, param2):
    """
    Una breve descripción de la función.

    Parameters
    ----------
    param1 : int
        El primer parámetro.
    param2 : str
        El segundo parámetro.

    Returns
    -------
    bool
        El valor de retorno.
    """
    pass

Estilo reStructuredText (reST)

def f(param1, param2):
    """
    Una breve descripción de la función.

    :param param1: El primer parámetro.
    :type param1: int
    :param param2: El segundo parámetro.
    :type param2: str
    :returns: El valor de retorno.
    :rtype: bool
    """
    pass

Variables Especiales y Útiles

name

En Python, name es una variable especial que se define automáticamente. Su valor depende de cómo se ejecuta el archivo:

  • Si el archivo se ejecuta directamente, name es igual a “main”.
  • Si el archivo se importa como un módulo en otro archivo, name toma el nombre del archivo (sin la extensión .py).

¿Por qué usar if name == "main":?

El uso de esta construcción permite definir una sección de código que solo se ejecutará cuando el archivo se ejecute directamente, y no cuando se importe como módulo. Esto es útil para escribir scripts que pueden funcionar tanto como programas independientes como módulos reutilizables.

# mi_script.py
def saludo():
    print("¡Hola, Mundo!")

if __name__ == "__main__":
    saludo()
# Si ejecutas mi_script.py directamente, la salida será ¡Hola, Mundo!.
# Si importas mi_script.py en otro archivo, no se ejecutará automáticamente saludo().
¡Hola, Mundo!

sys.argv

El módulo sys proporciona acceso a algunos objetos utilizados o mantenidos por el intérprete de Python y a funciones que interaccionan fuertemente con el intérprete.

import sys

# Lista en la que cada elemento es un argumento pasado al script
def main():
    print("Nombre del script:", sys.argv[0])
    for i, arg in enumerate(sys.argv[1:], start = 1):
        print(f"Argumento {i}: {arg}")

if __name__ == "__main__":
    main()
Nombre del script: C:\Users\Jason\AppData\Roaming\Python\Python312\site-packages\ipykernel_launcher.py
Argumento 1: -f
Argumento 2: C:\Users\Jason\AppData\Local\Temp\tmp9upt3ui7.json
Argumento 3: --HistoryManager.hist_file=:memory:

os.environ

El módulo os permite interactuar con el sistema operativo.

import os

# Diccionario que contiene las variables de entorno del sistema
def mostrar_variables_entorno():
    for clave, valor in os.environ.items():
        print(f"{clave}: {valor}")

if __name__ == "__main__":
    mostrar_variables_entorno()
ALLUSERSPROFILE: C:\ProgramData
APPDATA: C:\Users\Jason\AppData\Roaming
CLICOLOR_FORCE: 1
COMMONPROGRAMFILES: C:\Program Files\Common Files
COMMONPROGRAMFILES(X86): C:\Program Files (x86)\Common Files
COMMONPROGRAMW6432: C:\Program Files\Common Files
COMPUTERNAME: JEISONALARCON
COMSPEC: C:\Windows\system32\cmd.exe
CURL_CA_BUNDLE: C:/PROGRA~1/R/R-44~1.0PA/etc/curl-ca-bundle.crt
DENO_DOM_PLUGIN: C:\PROGRA~1\Quarto\bin\tools\x86_64\deno_dom\plugin.dll
DENO_NO_UPDATE_CHECK: 1
DENO_TLS_CA_STORE: system,mozilla
DISPLAY: :0
DRIVERDATA: C:\Windows\System32\Drivers\DriverData
EFC_5056: 1
GFORTRAN_STDERR_UNIT: -1
GFORTRAN_STDOUT_UNIT: -1
GIT_ASKPASS: rpostback-askpass
HOME: C:\Users\Jason\OneDrive\Documents
HOMEDRIVE: C:
HOMEPATH: \Users\Jason
IPY_INTERRUPT_EVENT: 1116
JPY_INTERRUPT_EVENT: 1116
JPY_PARENT_PID: 852
LOCALAPPDATA: C:\Users\Jason\AppData\Local
LOGONSERVER: \\JEISONALARCON
MPLBACKEND: module://matplotlib_inline.backend_inline
MPLENGINE: tkAgg
MSYS2_ENV_CONV_EXCL: R_ARCH
NO_COLOR: TRUE
NUMBER_OF_PROCESSORS: 16
ONEDRIVE: C:\Users\Jason\OneDrive
ORIGINAL_XDG_CURRENT_DESKTOP: undefined
OS: Windows_NT
PATH: C:\rtools44\x86_64-w64-mingw32.static.posix\bin;C:\rtools44\usr\bin;C:\Program Files\R\R-4.4.0patched\bin\x64;C:\Program Files\Quarto\bin;C:\Program Files\Python312\Scripts\;C:\Program Files\Python312\;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Program Files\Git\cmd;C:\Users\Jason\AppData\Local\Microsoft\WindowsApps;C:\Users\Jason\AppData\Local\Programs\Microsoft VS Code\bin;C:\Users\Jason\AppData\Local\Programs\MiKTeX\miktex\bin\x64\;C:\Program Files\Quarto\bin;C:\Program Files\RStudio\resources\app\bin\postback
PATHEXT: .COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.PY;.PYW
PROCESSOR_ARCHITECTURE: AMD64
PROCESSOR_IDENTIFIER: AMD64 Family 25 Model 116 Stepping 1, AuthenticAMD
PROCESSOR_LEVEL: 25
PROCESSOR_REVISION: 7401
PROGRAMDATA: C:\ProgramData
PROGRAMFILES: C:\Program Files
PROGRAMFILES(X86): C:\Program Files (x86)
PROGRAMW6432: C:\Program Files
PSMODULEPATH: C:\Program Files\WindowsPowerShell\Modules;C:\Windows\system32\WindowsPowerShell\v1.0\Modules
PUBLIC: C:\Users\Public
PYDEVD_DISABLE_FILE_VALIDATION: 1
PYTHONIOENCODING: utf-8
PY_PYTHON: C:\ProgramData\miniconda3\
QUARTO_BIN_PATH: C:\PROGRA~1\Quarto\bin
QUARTO_DENO: C:\PROGRA~1\Quarto\bin\tools\x86_64\deno
QUARTO_DOCUMENT_PATH: D:\Documentos\JOBS\DNPE\CursoPython\posts\Getting Started
QUARTO_FIG_DPI: 192
QUARTO_FIG_FORMAT: png
QUARTO_FIG_HEIGHT: 5
QUARTO_FIG_WIDTH: 7
QUARTO_PROFILE: 
QUARTO_PROJECT_ROOT: D:\Documentos\JOBS\DNPE\CursoPython
QUARTO_PYTHON: C:\ProgramData\miniconda3\
QUARTO_RENDER_TOKEN: f20dd7d5-943c-414a-9790-0d782f256425
QUARTO_SHARE_PATH: C:\PROGRA~1\Quarto\share
RETICULATE_PYTHON: C:\ProgramData\miniconda3\
RMARKDOWN_MATHJAX_PATH: C:/Program Files/RStudio/resources/app/resources/mathjax-27
RSTUDIO: 1
RSTUDIO_CHILD_PROCESS_PANE: job
RSTUDIO_CLI_HYPERLINKS: true
RSTUDIO_CONSOLE_COLOR: 256
RSTUDIO_CONSOLE_WIDTH: 193
RSTUDIO_DESKTOP_EXE: C:\Program Files\RStudio\rstudio.exe
RSTUDIO_MSYS_SSH: C:/Program Files/RStudio/resources/app/bin/msys-ssh-1000-18
RSTUDIO_PANDOC: C:/Program Files/RStudio/resources/app/bin/quarto/bin/tools
RSTUDIO_PROGRAM_MODE: desktop
RSTUDIO_SESSION_PID: 9388
RSTUDIO_SESSION_PORT: 37453
RSTUDIO_USER_IDENTITY: Jason
RSTUDIO_WINUTILS: C:/Program Files/RStudio/resources/app/bin/winutils
RS_LOCAL_PEER: \\.\pipe\37453-rsession
RS_LOG_LEVEL: WARN
RS_RPOSTBACK_PATH: C:/Program Files/RStudio/resources/app/bin/rpostback.exe
RS_SHARED_SECRET: 3a833e1a-f1b9-434a-8e77-092fec2a585e
RTOOLS44_HOME: C:\rtools44
R_ARCH: /x64
R_CLI_HAS_HYPERLINK_IDE_HELP: true
R_CLI_HAS_HYPERLINK_IDE_RUN: true
R_CLI_HAS_HYPERLINK_IDE_VIGNETTE: true
R_COMPILED_BY: gcc 13.2.0
R_DOC_DIR: C:/PROGRA~1/R/R-44~1.0PA/doc
R_HOME: C:/PROGRA~1/R/R-44~1.0PA
R_INCLUDE_DIR: C:/PROGRA~1/R/R-44~1.0PA/include
R_LIBS_SITE: C:/PROGRA~1/R/R-44~1.0PA/site-library
R_LIBS_USER: C:\Users\Jason\AppData\Local/R/win-library/4.4
R_PLATFORM: 
R_RTOOLS44_PATH: C:\rtools44/x86_64-w64-mingw32.static.posix/bin;C:\rtools44/usr/bin
R_RUNTIME: ucrt
R_SHARE_DIR: C:/PROGRA~1/R/R-44~1.0PA/share
R_USER: C:/Users/Jason/OneDrive/Documents
SESSIONNAME: Console
SSH_ASKPASS: rpostback-askpass
SYSTEMDRIVE: C:
SYSTEMROOT: C:\Windows
TEMP: C:\Users\Jason\AppData\Local\Temp
TERM: xterm-color
TMP: C:\Users\Jason\AppData\Local\Temp
USERDOMAIN: JEISONALARCON
USERDOMAIN_ROAMINGPROFILE: JEISONALARCON
USERNAME: Jason
USERPROFILE: C:\Users\Jason
WINDIR: C:\Windows
PYDEVD_USE_FRAME_EVAL: NO
CLICOLOR: 1
FORCE_COLOR: 1
PAGER: cat
GIT_PAGER: cat

Python VS. R

cadena = "Hola, Mundo!"
# Convertir a minúsculas
print(cadena.lower())                     # tolower(cadena)

# Reemplazar caracteres
print(cadena.replace("Mundo", "Python"))  # sub("Mundo", "R", cadena)

# Dividir cadenas
print(cadena.split(", "))                 # strsplit(cadena, ", ")

# Comprensiones de Listas
print([x ** 2 for x in range(10)])        # sapply(0:9, function(x) x^2)

Funciones Anidadas y Clausuras

def exterior(x):
    def interior(y):
        return x + y
    return interior

print(exterior(5)) 
#| eval: false
exterior <- function(x) {
    interior <- function(y) {
        x + y
    }
    interior
}

exterior(5)

Creación de Factores

sexo = ["M", "F", "F", "M", "M"]
niveles = {"M": "Male", "F": "Female"}
print([niveles[s] for s in sexo])
#| eval: false
sexo <- factor(c("M", "F", "F", "M", "M"))
levels(sexo) <- c("Female", "Male")
print(sexo)

Programación Orientada a Objetos (OOP)

class Coche:
    def __init__(self, marca, modelo):
        self.marca = marca
        self.modelo = modelo
    
    def descripcion(self):
        return f"Coche: {self.marca} {self.modelo}"

mi_coche = Coche("Toyota", "Corolla")
print(mi_coche.descripcion())  # Coche: Toyota Corolla
#| eval: false
setClass("Coche",
         slots = list(marca = "character", modelo = "character"))

setMethod("initialize", "Coche",
          function(.Object, marca, modelo) {
            .Object@marca <- marca
            .Object@modelo <- modelo
            .Object
          })

setMethod("descripcion", "Coche",
          function(object) {
            paste("Coche:", object@marca, object@modelo)
          })

mi_coche <- new("Coche", marca = "Toyota", modelo = "Corolla")
descripcion(mi_coche)  # "Coche: Toyota Corolla"

Python Enhancement Proposal (PEP): https://peps.python.org/pep-0008/

https://docs.python.org/es/3/library/trace.html python -m trace –count -C . somefile.py Esto ejecutará somefile.py y generará listados anotados de todos los módulos de Python importados durante la ejecución en el directorio actual.

https://jjallaire.github.io/pydata-quarto-dashboards/#/learning-more

# import numpy as np
# import matplotlib.pyplot as plt
# import pandas as pd
# import seaborn as sns
# import statsmodels as sm
#
# pd.options.display.max_columns = 20
# pd.options.display.max_rows = 20
# pd.options.display.max_colwidth = 80
# np.set_printoptions(precision = 4, suppress = True)